package com.lntea.netty.demo.diff.echo.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Calendar;
import java.util.Iterator;
import java.util.Set;

/**
 * Title: NioEchoServerHandler.java<br>
 * Description:  nio多路复用服务端<br>
 * Copyright: Copyright (c) 2015<br>
 * Company: 北京云杉世界信息技术有限公司<br>
 *
 * @author lichao
 * @date 2019/11/5 12:09
 */
public class NioEchoServerHandler implements Runnable{
    /** 多路复用器 */
    private Selector selector;
    /** 服务端通道 */
    private ServerSocketChannel serverChannel;
    /** echo标识 */
    private String echoStr;

    /**
     * 初始化nio服务端
     * @param serverIp
     * @param serverPort
     */
    public NioEchoServerHandler(String serverIp, Integer serverPort, String echoStr) {
        try {
            // 1.打开多路复用器
            selector = Selector.open();
            // 2.打开服务端通道
            serverChannel = ServerSocketChannel.open();
            // 3.配置通道为非阻塞模式
            serverChannel.configureBlocking(false);
            // 4.打开套接字，绑定服务端ip和port
            serverChannel.socket().bind(new InetSocketAddress(serverIp, serverPort), 1024);
            // 5.将通道注册到多路复用器，监听连接建立事件
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println(String.format("Echo Server start at port %d", serverPort));
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
        this.echoStr = echoStr;
    }

    @Override
    public void run() {
        while (true) {
            try {
                // 每隔1秒读取一次事件队列
                selector.select(1000);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                // 遍历取出的事件
                Iterator<SelectionKey> ite = selectionKeys.iterator();
                while (ite.hasNext()) {
                    SelectionKey key = ite.next();
                    ite.remove();

                    try {
                        handleInput(key);
                    } catch (Exception e) {
                        if (key != null) {
                            key.cancel();
                            if (key.channel() != null) {
                                key.channel().close();
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 处理多路复用事件
     * @param key
     */
    private void handleInput(SelectionKey key) throws IOException {
        // 合法事件
        if (key.isValid()) {
            // 建立连接请求
            if (key.isAcceptable()) {
                // 1. 获取服务端通道
                ServerSocketChannel scc = (ServerSocketChannel) key.channel();
                // 2. 接收客户端请求，建立与客户端的连接通道
                SocketChannel sc = scc.accept();
                // 3. 配置非阻塞模式
                sc.configureBlocking(false);
                // 4. 注册多路复用器，监听数据读取事件
                sc.register(selector, SelectionKey.OP_READ);
            }

            // 数据读取请求
            if (key.isReadable()) {
                // 获取客户端连接通道
                SocketChannel sc = (SocketChannel) key.channel();
                // 创建读缓冲区
                ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                // 读入的字节数
                int readByteSize = sc.read(readBuffer);
                if (readByteSize > 0) {
                    // 重置读取指针位置
                    readBuffer.flip();
                    // 创建数组
                    byte[] bytes = new byte[readBuffer.remaining()];
                    // 读取数据
                    readBuffer.get(bytes);
                    String body = new String(bytes, "UTF-8");
                    // 处理客户端请求
                    System.out.println(String.format("Echo Server receive msg: %s", body));
                    String responseBody = "bad echo";
                    if (echoStr.equals(body)) {
                        responseBody = Calendar.getInstance().getTime().toString();
                    }

                    doWrite(sc, responseBody);
                }
            }
        }
    }

    /**
     * 输出返回
     * @param channel
     * @param responseBody
     */
    private void doWrite(SocketChannel channel, String responseBody) throws IOException {
        if (responseBody != null && responseBody.trim().length() > 0) {
            byte[] bytes = responseBody.getBytes();
            ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
            writeBuffer.put(bytes);
            writeBuffer.flip();
            channel.write(writeBuffer);
        }
    }
}
